#pragma once
#include<iostream>
#include<functional>
#include<cstring>
#include<cerrno>
#include<cstdio>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"error.hpp"

namespace ns_server
{
    //返回值是string，参数是string的函数
    using func_t = std::function<std::string (std::string)>;
    class UdpServer
    {
    public:
        UdpServer(int port,func_t service):_port(port),_service(service)
        {
            std::cout<<"server addr:"<<_port<<std::endl;
        }

        void InitServer()
        {
            //1. 创建套接字(实际是打开网络文件)
            //AF_INET表示网络通信，SOCK_DGRAM是UDP的数据报，第三个参数默认为0
            _sock=socket(AF_INET,SOCK_DGRAM,0);//_sock=3
            if(_sock<0)//_sock<0代表创建套接字失败
            {
                std::cerr<<"socket create fail:"<<strerror(errno)<<std::endl;
                exit(SOCKET_ERR);
            }
            //到此代表创建套接字成功
            std::cout<<"socket create success:"<<_sock<<std::endl;

            //2.绑定结构体   sockaddr_in网络结构体
            struct sockaddr_in local;
            bzero(&local,sizeof(local));//清空结构体
            
            local.sin_family=AF_INET;
            local.sin_port=htons(_port);//转大端

            //inet_addr
            //(1).既可以转大端
            //(2).又可以字符串转in_addr_t
            //local.sin_addr.s_addr=inet_addr(_ip.c_str());

            local.sin_addr.s_addr=INADDR_ANY;//可以接收任意IP地址

            //第一个参数是套接字，第二个参数是套接字结构体，第三个是结构体大小
            int n=bind(_sock,(struct sockaddr*)&local,sizeof(local));
            if(n<0)
            {
                std::cerr<<"bind fail:"<<strerror(errno)<<std::endl;
                exit(BIND_ERR);
            }
            std::cout<<"bind success:"<<_sock<<std::endl;
        }

        //启动服务器
        void Start()
        {
            //业务：接收用户发来的数据，并回应
            char buffer[2048];
            while(true)
            {
                //用户的套接字，通信需要记录是谁发的
                struct sockaddr_in peer;
                memset(&peer,0,sizeof(peer));
                socklen_t len=sizeof(peer);

                //1.收信息
                //第一参数是网络文件描述符，第二参数是缓冲区，第三参数是缓冲区大小，预留一个/0
                //第四参数是阻塞读，第五参数是用户的套接字，要通信
                int n=recvfrom(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
                if(n<0)
                    continue;
                else 
                    buffer[n]='\0';
                
                std::cout<<peer.sin_addr.s_addr<<" # "<<buffer<<std::endl;
                //业务处理
                std::string message=_service(buffer);

                //2.回消息
                //第一参数是网络文件描述符，第二参数是消息，第三参数是消息大小，不需要/0
                //第四参数是阻塞读，第五参数是发给谁
                sendto(_sock,message.c_str(),message.size(),0,(struct sockaddr*)&peer,sizeof(peer));
            }
        }

        ~UdpServer(){}
        
    private:
        int _sock;//套接字
        uint16_t _port;//端口号
        func_t _service;//业务处理

        //服务器不能绑定特定IP
        //因为一个主机可能有多个网卡，多个IP，如果只绑定某一个，那接受的数据就少了
        //std::string _ip;//IP地址
    };
}